package io.kiki.stack.http.feign;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import static io.kiki.stack.http.feign.Util.checkNotNull;

class ReflectiveFeignInvocationHandlerImpl implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> methodToMethodHandlerMap;

    ReflectiveFeignInvocationHandlerImpl(Target target, Map<Method, MethodHandler> methodToMethodHandlerMap) {
        this.target = checkNotNull(target, "target");
        this.methodToMethodHandlerMap = checkNotNull(methodToMethodHandlerMap, "dispatch for %s", target);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("equals".equals(method.getName())) {
            try {
                Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
            }
        } else
            if ("hashCode".equals(method.getName())) {
                return hashCode();
            } else
                if ("toString".equals(method.getName())) {
                    return toString();
                } else
                    if (!methodToMethodHandlerMap.containsKey(method)) {
                        throw new UnsupportedOperationException(String.format("Method \"%s\" should not be called", method.getName()));
                    }

        return methodToMethodHandlerMap.get(method).invoke(args);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ReflectiveFeignInvocationHandlerImpl other) {
            return target.equals(other.target);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return target.hashCode();
    }

    @Override
    public String toString() {
        return target.toString();
    }
}
